import java.math.BigInteger;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.atomic.AtomicInteger;

public class NTPMain {

	/**
	 * Spisak servera na koji ce se klijent nakaciti da bi dobio vremena
	 * 
	 * Spisak servera uzet od Aleksandra Stancica
	 */
	public static final String[] servers = { "2.rs.pool.ntp.org",
			"3.europe.pool.ntp.org", "2.europe.pool.ntp.org",
			"0.ubuntu.pool.ntp.org", "1.ubuntu.pool.ntp.org",
			"2.ubuntu.pool.ntp.org", "3.ubuntu.pool.ntp.org" };
	public static NTPRequestor[] requestors = new NTPRequestor[servers.length];
	
	public static void main(String[] args) {
		NTPMain main = new NTPMain();
		Counter counter = main.new Counter();	
		for (int i = 0; i < servers.length; i++) {
			new Thread(requestors[i] = main.new NTPRequestor(servers[i], counter)).start();
		}
	}

	

	/**
	 * Klasa iz koje se salje request i koja skuplja sve bitne podatke
	 * @author Marija
	 *
	 */
	public class NTPRequestor implements Runnable {

		String address;
		//offseti na kojima pocinju sekunde
		int[] offsets = { 16, 24, 32, 40 };
		//offseti na kojima pocinju milisekunde
		int[] fractionOffsets = { 20, 28, 36, 44 };
		//vreme u milisekundama kada je poslat zahtev, kada je stigao, i mrezno kasnjenje
		long startTime, endTime, networkDelay;
		//reference timestamp, origin timestamp, recieve timestamp i transmit timestamp
		Date[] dates = new Date[4];
		private Counter counter;

		public NTPRequestor(String address, Counter counter) {
			this.address = address;
			this.counter = counter;
		}

		@Override
		public void run() {
			UDPClient client = new UDPClient();
			try {
				startTime = System.currentTimeMillis();
				byte[] b = client.Open(address);
				endTime = System.currentTimeMillis();
				for (int j = 0; j < offsets.length; j++) {
					long seconds = NTPUtil.bytesToLong(b, offsets[j]);
					long fraction = NTPUtil.bytesToLong(b, fractionOffsets[j]);
					switch (j) {
					case 0:
						System.out.print("Reference timestamp: ");
						break;
					case 1:
						System.out.print("Origin timestamp: ");
						break;
					case 2:
						System.out.print("Recieve timestamp: ");
						break;
					case 3:
						System.out.print("Transmit timestamp: ");
						break;
					}
					Date d = NTPUtil.dateFromTimestamp(seconds, fraction);
					dates[j] = d;
					System.out.println(d);
				}
				//merenje zakasnjenja zbog mreze;
				networkDelay = (dates[3].getTime() - dates[2].getTime())/2 + (endTime - startTime)/2;
				counter.increment();
				
				System.out.println("Network delay: " + networkDelay);
			} catch (Exception e) {
				e.printStackTrace();
			}
			

		}

	}

	/**
	 * Vodi racuna o odgovorima i kad svi stignu samo ih skupi
	 * @author Marija
	 *
	 */
	public class Counter {
		int counter;

		/**
		 * Po skupljenim podacima, agregira datum
		 */
		public synchronized void increment() {
			System.out.println("Incrementing counter " + counter + ", " + NTPMain.servers.length);
			if (counter == NTPMain.servers.length - 1) {
				gatherAll();
				return;
			}
			counter++;
		}

		/**
		 * Posle svih skupljenih odgovora, izracuna aritmeticku sredinu
		 * pri racunanju samo svaki network delay oduzme
		 */
		public void gatherAll() {
			BigInteger integer = new BigInteger("0");
			for(int i = 0 ;i < requestors.length; i++)
			{
				long diff = requestors[i].dates[3].getTime() - requestors[i].networkDelay;
				
				integer = integer.add(new BigInteger(diff + ""));
			}
			
			integer = integer.divide(new BigInteger(requestors.length + ""));
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSS");
			String df = sdf.format(new Date(integer.longValue()));
			System.out.println(df);

			System.out.println("Correct timing is: " + new Date(integer.longValue()));
		}
	}

}
